<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Trait</title>
</head>
<body>
    <h1>Trait</h1>
    <h2>一种代码复用的方式，类似于Mixin</h2>

    <script>
        // Trait的基本实现
        const TLogger = {
            log(message) {
                console.log(`[${new Date().toISOString()}] ${message}`);
            }
        };

        const TEventEmitter = {
            on(event, handler) {
                this._events = this._events || {};
                (this._events[event] = this._events[event] || []).push(handler);
            },
            emit(event, data) {
                if (!this._events || !this._events[event]) return;
                this._events[event].forEach(handler => handler(data));
            }
        };

        // 使用装饰器应用Trait
        function traits(...traitsList) {
            return function(target) {
                Object.assign(target.prototype, ...traitsList);
            };
        }

        // 实际应用示例
        // 1. 日志和事件功能
        @traits(TLogger, TEventEmitter)
        class User {
            constructor(name) {
                this.name = name;
            }

            updateProfile(data) {
                Object.assign(this, data);
                this.log(`用户 ${this.name} 更新了资料`);
                this.emit('profileUpdated', data);
            }
        }

        // 2. 持久化Trait
        const TPersistable = {
            save() {
                const data = JSON.stringify(this);
                localStorage.setItem(this.constructor.name + this.id, data);
                return this;
            },
            load(id) {
                const data = localStorage.getItem(this.constructor.name + id);
                return data ? Object.assign(this, JSON.parse(data)) : null;
            }
        };

        // 3. 验证Trait
        const TValidatable = {
            validate() {
                const errors = [];
                for (const [field, rules] of Object.entries(this.validationRules)) {
                    if (!rules.test(this[field])) {
                        errors.push(`${field} 验证失败`);
                    }
                }
                return errors;
            }
        };

        @traits(TPersistable, TValidatable)
        class Product {
            constructor(id, name, price) {
                this.id = id;
                this.name = name;
                this.price = price;
            }

            validationRules = {
                name: /^.{3,50}$/,
                price: /^\d+(\.\d{1,2})?$/
            };
        }

        // 4. 组合多个Trait
        const TSerializable = {
            toJSON() {
                const proto = Object.getPrototypeOf(this);
                const jsonObj = Object.assign({}, this);
                
                Object.entries(proto)
                    .filter(([key]) => typeof this[key] === 'function')
                    .forEach(([key]) => {
                        delete jsonObj[key];
                    });
                
                return jsonObj;
            },
            fromJSON(json) {
                return Object.assign(this, JSON.parse(json));
            }
        };

        @traits(TLogger, TEventEmitter, TPersistable, TSerializable)
        class Order {
            constructor(id, items = []) {
                this.id = id;
                this.items = items;
                this.createdAt = new Date();
            }

            addItem(item) {
                this.items.push(item);
                this.log(`添加商品: ${item.name}`);
                this.emit('itemAdded', item);
                this.save();
            }
        }
    </script>
</body>
</html>  